from math import sin, cos, pi
from pandac.PandaModules import Vec3
# CMDID
from Body.Command import CMD_UP,CMD_DOWN,CMD_LEFT,CMD_RIGHT
# define
from Body.Define import SCREEN_X, SCREEN_Y
# interval
from direct.interval.IntervalGlobal import LerpHprInterval, LerpFunc
from Body.Utility import IntervalsTurnOff

DEG_TO_RAD = pi/180 #translates degrees to radians for sin and cos
DefaultParameter= dict(
    AccelRation = 1,    #Ship acceleration in units/sec/sec
    MaxVel = 1,         #Maximum ship velocity in units/sec
    TurnRate = 60       #Degrees ship can turn in 1 second
)

class MassBase:
    def __init__(self, model, **kw ):
        self.Model = model
        # parameter
        paramDict = DefaultParameter.copy()
        paramDict.update(kw)
        self.AccelRation = paramDict["AccelRation"]
        self.MaxVel = paramDict["MaxVel"]
        self.MaxVelSQ = self.MaxVel ** 2
        self.TurnRate = paramDict["TurnRate"]
        self.ModelRadius = 0.5 * self.Model.getScale().getX()
        # member
        self.Heading = self.Model.getR()  #Heading is the roll value for this model
        self.Pos = self.Model.getPos()  # initial pos
        self.Velocity = Vec3(0,    0,0)
        # interval
        self.HasNewPosInterval = False
        self.HasNewHprInterval = False
        self.ModelPosInterval = None
        self.ModelHprInterval = None
        self.IntervalList = [self.ModelPosInterval, self.ModelHprInterval]

        self.InputState = {
            CMD_UP:0,
            CMD_DOWN:0,
            CMD_LEFT:0,
            CMD_RIGHT:0}

    # logic update fn
    def Update(self, dt):
        inputState = self.InputState
        accel = inputState[CMD_UP] - inputState[CMD_DOWN]
        turnRight = inputState[CMD_RIGHT] - inputState[CMD_LEFT]

        self._updateHeading(dt, accel, turnRight)
        self._updateVelocity(dt, accel)
        self._updatePosition(dt)
        self._updateInterval(dt)

    def _updateHeading(self, dt, accel, turnRight):
        if (accel!=0) and (turnRight!=0):
            self.Heading %= 360
            self._OldHeading = self.Heading
            self.Heading += (dt * self.TurnRate * turnRight)
            self.HasNewHprInterval = True
        else:
            self.HasNewHprInterval = False

    def _updateVelocity(self, dt, accel):
        if accel!= 0:
            heading_rad = DEG_TO_RAD * self.Heading
            newVel = (Vec3(sin(heading_rad), 0, cos(heading_rad)) * accel) * (self.AccelRation * dt)
            newVel += self.Velocity
            if newVel.lengthSquared() > self.MaxVelSQ:
                newVel.normalize()
                newVel *= self.MaxVel
            self.Velocity = newVel

    def _updatePosition(self, dt):
        if self.Velocity != Vec3.zero():
            self._OldPos = self.Pos
            self._DeltaPos = self.Velocity * dt
            self.Pos += self._DeltaPos
            self._posWrapper(self.Pos)
            #print self.Pos
            self.HasNewPosInterval = True
        else:
            self.HasNewPosInterval = False

    def _updateInterval(self, dt):
        IntervalsTurnOff(self.IntervalList)
        if self.HasNewHprInterval:
            self.ModelHprInterval = LerpHprInterval(self.Model, dt, (0,0,self.Heading), (0,0,self._OldHeading))
            self.ModelHprInterval.start()
        if self.HasNewPosInterval:
            self.ModelPosInterval = LerpFunc(self._updatePosLerpFn, duration = dt,
                                             extraArgs = [self._DeltaPos])
            self.ModelPosInterval.start()

    def _updatePosLerpFn(self, dt, deltaPos):
        newPos = self._OldPos + (deltaPos * dt)
        self._posWrapper(newPos)
        self.Model.setPos(newPos)

    def _posWrapper(self, pos):
        posX, posZ = pos.getX(), pos.getZ()
        radius = self.ModelRadius
        if ((posX - radius) > SCREEN_X) or \
           ((posX + radius) < -SCREEN_X):
            pos.setX(-posX)
        if ((posZ - radius) > SCREEN_Y) or \
           ((posZ + radius) < -SCREEN_Y):
            pos.setZ(-posZ)

    def CmdInput(self, cmd, value):
        self.InputState[cmd] = value

    def DoDestroy(self):
        IntervalsTurnOff(self.IntervalList)
